/* sm_boat.c -- O barco
 *
 * Copyright (C) 2008, Diogo F. S. Ramos <diogo.ramos@usp.br>
 *                     Márcio Rogério
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdlib.h>
#include <math.h> 
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "sm_boat.h"
#include "sm_utils.h"

sm_boat_t *
sm_boat_new(void)
{
    sm_boat_t * boat = NULL;

    boat = XMALLOC( sm_boat_t, 1 );

    boat->draw = (void*) &sm_boat_draw;

    /* Posição */
    boat->pos[0] = 0.0f;
    boat->pos[1] = 0.0f;
    boat->pos[2] = 0.0f;
    boat->angle = 0.0f;
    boat->angle_inc = 0.5f;

    /* Bounce */
    boat->bounce_inc = 0.1f;
    
    /* Base */
    boat->base_cube_size = 29.87;

    boat->base_mat_specular[POS_RED]   = 0.0;
    boat->base_mat_specular[POS_GREEN] = 0.0;
    boat->base_mat_specular[POS_BLUE]  = 0.0;
    boat->base_mat_specular[POS_ALPHA] = 1.0;
    boat->base_mat_diffuse[POS_RED]    = 0.60;
    boat->base_mat_diffuse[POS_GREEN]  = 0.20;
    boat->base_mat_diffuse[POS_BLUE]   = 0.0;
    boat->base_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->base_mat_ambient[POS_RED]    = 0.0;
    boat->base_mat_ambient[POS_GREEN]  = 0.0;
    boat->base_mat_ambient[POS_BLUE]   = 0.0;
    boat->base_mat_ambient[POS_ALPHA]  = 1.0;
    boat->base_mat_shininess           = 50.0;

    boat->cargo_door_angle = 0.0;

    /* Mastro */
    boat->mastro = gluNewQuadric();
    boat->mastro_radius_base = 0.3;
    boat->mastro_radius_top = 0.15;
    boat->mastro_height = 19.81;

    boat->mastro_mat_specular[POS_RED]   = 1.0;
    boat->mastro_mat_specular[POS_GREEN] = 1.0;
    boat->mastro_mat_specular[POS_BLUE]  = 1.0;
    boat->mastro_mat_specular[POS_ALPHA] = 1.0;
    boat->mastro_mat_diffuse[POS_RED]    = 0.43;
    boat->mastro_mat_diffuse[POS_GREEN]  = 0.09;
    boat->mastro_mat_diffuse[POS_BLUE]   = 0.0;
    boat->mastro_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->mastro_mat_ambient[POS_RED]    = 1.0;
    boat->mastro_mat_ambient[POS_GREEN]  = 1.0;
    boat->mastro_mat_ambient[POS_BLUE]   = 1.0;
    boat->mastro_mat_ambient[POS_ALPHA]  = 1.0;
    boat->mastro_mat_shininess           = 100.0;


    /* Mastro Secundario */
    boat->mastro_sec = gluNewQuadric();
    boat->mastro_sec_radius_base = 0.15;
    boat->mastro_sec_radius_top = 0.1;
    boat->mastro_sec_height = 15.81;

    boat->mastro_sec_mat_specular[POS_RED]   = 1.0;
    boat->mastro_sec_mat_specular[POS_GREEN] = 1.0;
    boat->mastro_sec_mat_specular[POS_BLUE]  = 1.0;
    boat->mastro_sec_mat_specular[POS_ALPHA] = 1.0;
    boat->mastro_sec_mat_diffuse[POS_RED]    = 0.43;
    boat->mastro_sec_mat_diffuse[POS_GREEN]  = 0.09;
    boat->mastro_sec_mat_diffuse[POS_BLUE]   = 0.0;
    boat->mastro_sec_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->mastro_sec_mat_ambient[POS_RED]    = 1.0;
    boat->mastro_sec_mat_ambient[POS_GREEN]  = 1.0;
    boat->mastro_sec_mat_ambient[POS_BLUE]   = 1.0;
    boat->mastro_sec_mat_ambient[POS_ALPHA]  = 1.0;
    boat->mastro_sec_mat_shininess           = 100.0;

    /* Mastro Frontal */
    boat->mastro_frontal = gluNewQuadric();
    boat->mastro_frontal_radius_base = 0.3;
    boat->mastro_frontal_radius_top = 0.15;
    boat->mastro_frontal_height = 10;

    boat->mastro_frontal_mat_specular[POS_RED]   = 1.0;
    boat->mastro_frontal_mat_specular[POS_GREEN] = 1.0;
    boat->mastro_frontal_mat_specular[POS_BLUE]  = 1.0;
    boat->mastro_frontal_mat_specular[POS_ALPHA] = 1.0;
    boat->mastro_frontal_mat_diffuse[POS_RED]    = 0.43;
    boat->mastro_frontal_mat_diffuse[POS_GREEN]  = 0.09;
    boat->mastro_frontal_mat_diffuse[POS_BLUE]   = 0.0;
    boat->mastro_frontal_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->mastro_frontal_mat_ambient[POS_RED]    = 1.0;
    boat->mastro_frontal_mat_ambient[POS_GREEN]  = 1.0;
    boat->mastro_frontal_mat_ambient[POS_BLUE]   = 1.0;
    boat->mastro_frontal_mat_ambient[POS_ALPHA]  = 1.0;
    boat->mastro_frontal_mat_shininess           = 100.0;

    /* Deck */
    boat->deck_cube_size = 6;

    boat->deck_mat_specular[POS_RED]   = 0.0;
    boat->deck_mat_specular[POS_GREEN] = 0.0;
    boat->deck_mat_specular[POS_BLUE]  = 0.0;
    boat->deck_mat_specular[POS_ALPHA] = 1.0;
    boat->deck_mat_diffuse[POS_RED]    = 0.60;
    boat->deck_mat_diffuse[POS_GREEN]  = 0.20;
    boat->deck_mat_diffuse[POS_BLUE]   = 0.0;
    boat->deck_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->deck_mat_ambient[POS_RED]    = 0.0;
    boat->deck_mat_ambient[POS_GREEN]  = 0.0;
    boat->deck_mat_ambient[POS_BLUE]   = 0.0;
    boat->deck_mat_ambient[POS_ALPHA]  = 1.0;
    boat->deck_mat_shininess           = 100.0;


    /* Keel */
    boat->keel_cube_size = 7.92;
    boat->keel_angle = 0.0;

    boat->keel_mat_specular[POS_RED]   = 0.0;
    boat->keel_mat_specular[POS_GREEN] = 0.0;
    boat->keel_mat_specular[POS_BLUE]  = 0.0;
    boat->keel_mat_specular[POS_ALPHA] = 1.0;
    boat->keel_mat_diffuse[POS_RED]    = 0.60;
    boat->keel_mat_diffuse[POS_GREEN]  = 0.20;
    boat->keel_mat_diffuse[POS_BLUE]   = 0.0;
    boat->keel_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->keel_mat_ambient[POS_RED]    = 0.0;
    boat->keel_mat_ambient[POS_GREEN]  = 0.0;
    boat->keel_mat_ambient[POS_BLUE]   = 0.0;
    boat->keel_mat_ambient[POS_ALPHA]  = 1.0;
    boat->keel_mat_shininess           = 100.0;


    /* Motor */
    boat->motor_prot_1 = gluNewQuadric();
    boat->motor_prot_2 = gluNewQuadric();
    boat->motor_prot_radius_base = 1.8;
    boat->motor_prot_radius_top = 1.8;
    boat->motor_prot_height = 0.5;
    boat->motor_hel_cube_size = 1.6*2;
    boat->motor_speed = 0;
    boat->motor_angle = 0.0;
    boat->motor_reverse = +1;
    boat->motor_turbo_1 = gluNewQuadric();
    boat->motor_turbo_2 = gluNewQuadric();
    boat->motor_turbo_radius_base = 1.8;
    boat->motor_turbo_radius_top = 1.8;
    boat->motor_turbo_height = 0.8;

	
    boat->motor_mat_specular[POS_RED]   = 1.0;
    boat->motor_mat_specular[POS_GREEN] = 1.0;
    boat->motor_mat_specular[POS_BLUE]  = 1.0;
    boat->motor_mat_specular[POS_ALPHA] = 1.0;
    boat->motor_mat_diffuse[POS_RED]    = 0.43;
    boat->motor_mat_diffuse[POS_GREEN]  = 0.09;
    boat->motor_mat_diffuse[POS_BLUE]   = 0.0;
    boat->motor_mat_diffuse[POS_ALPHA]  = 1.0;
    boat->motor_mat_ambient[POS_RED]    = 1.0;
    boat->motor_mat_ambient[POS_GREEN]  = 1.0;
    boat->motor_mat_ambient[POS_BLUE]   = 1.0;
    boat->motor_mat_ambient[POS_ALPHA]  = 1.0;
    boat->motor_mat_shininess           = 100.0;

    boat->anchored = 0;
    
    return boat;
}

void
sm_boat_paint_mastro( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->mastro_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->mastro_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->mastro_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->mastro_mat_shininess);
}

void
sm_boat_paint_mastro_sec( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->mastro_sec_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->mastro_sec_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->mastro_sec_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->mastro_sec_mat_shininess);
}

void
sm_boat_paint_mastro_frontal( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->mastro_frontal_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->mastro_frontal_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->mastro_frontal_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->mastro_frontal_mat_shininess);
}

void
sm_boat_paint_keel( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->keel_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->keel_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->keel_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->keel_mat_shininess);
}

void
sm_boat_paint_deck( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->deck_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->deck_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->deck_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->deck_mat_shininess);
}

void
sm_boat_paint_base( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, self->base_mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, self->base_mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, self->base_mat_diffuse);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, self->base_mat_shininess);
}

void
sm_boat_paint_motor( sm_boat_t * self )
{
    glMaterialfv(GL_FRONT, GL_SPECULAR, self->motor_mat_specular);
    glMaterialfv(GL_FRONT, GL_AMBIENT, self->motor_mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, self->motor_mat_diffuse);
    glMaterialf(GL_FRONT, GL_SHININESS, self->motor_mat_shininess);
}

void
sm_boat_toggle_anchor( sm_boat_t * self )
{
    self->anchored = ~self->anchored;
}

void
sm_boat_motor_turn( sm_boat_t * self)
{
    self->motor_angle += self->motor_speed*self->motor_reverse;

    if( (self->motor_angle > 360.0) || (self->motor_angle < -360.0) )
	self->motor_angle = 0.0;

    if( self->motor_speed > 0 && !(self->anchored) )
	sm_boat_sail_forward( self );
}

void
sm_boat_bounce( sm_boat_t * self )
{
    if( (self->pos[1] > 1.0f) || (self->pos[1] < -1.0f) )
	self->bounce_inc *= -1;

    self->pos[1] += self->bounce_inc;
}

void
sm_boat_turn_left( sm_boat_t * self )
{
    if( self->angle > 360 )
	self->angle = 0;
    self->angle += self->angle_inc;
}

void
sm_boat_turn_right( sm_boat_t * self )
{
    if( self->angle < -360 )
	self->angle = 0;
    self->angle -= self->angle_inc;
}

void
sm_boat_sail_forward( sm_boat_t * self )
{
    int i = 0;
    
    self->pos[0] += (GLfloat) cos( (self->angle*M_PI)/180.0 ) *
	(self->motor_speed/10 + 0.9-sm_boat_motor_turbo_get(self)) *
	(self->motor_reverse);
    self->pos[2] -= (GLfloat) sin( (self->angle*M_PI)/180.0 ) *
	(self->motor_speed/10 + 0.9-sm_boat_motor_turbo_get(self)) *
	(self->motor_reverse);

    if( self->keel_angle  > 0.0 )
    {
	for( i = 0 ; i < self->keel_angle/10 ; i++ )
	{
	    if(self->motor_reverse > 0)
		sm_boat_turn_right( self);
	    else
		sm_boat_turn_left( self);		
	}
    }else if( self->keel_angle < 0.0 )
    {
	for( i = 0 ; i < abs(self->keel_angle/10) ; i++ )
	{
	    if(self->motor_reverse > 0)
		sm_boat_turn_left( self);
	    else
		sm_boat_turn_right( self);		
	}
    }
}

void
sm_boat_cargo_door_move( sm_boat_t * self, const int direction )
{
    self->cargo_door_angle += direction*2;

    if( self->cargo_door_angle > 0.0 )
	self->cargo_door_angle = 0.0;
    if( self->cargo_door_angle < -45.0 )
	self->cargo_door_angle = -45.0;    
}

void
sm_boat_keel_move( sm_boat_t * self, const int direction )
{
    self->keel_angle += direction*2;

    if( self->keel_angle > 55.0 )
	self->keel_angle = 55.0;
    if( self->keel_angle < -55.0 )
	self->keel_angle = -55.0;    
}

void
sm_boat_motor_set_speed( sm_boat_t * self, const int speed )
{
    self->motor_speed = speed*10;
}

void
sm_boat_motor_toggle_reverse( sm_boat_t * self )
{
    if( self->motor_reverse > 0 )
	self->motor_reverse = -1;
    else
	self->motor_reverse = +1;
}

GLfloat
sm_boat_motor_turbo_get( sm_boat_t * self )
{
    return self->motor_turbo_radius_top;
}

void
sm_boat_motor_turbo_increase( sm_boat_t * self )
{
    self->motor_turbo_radius_top -= 0.1;

    if( self->motor_turbo_radius_top < 0.9 )
	self->motor_turbo_radius_top = 0.9;
}

void
sm_boat_motor_turbo_decrease( sm_boat_t * self )
{
    self->motor_turbo_radius_top += 0.1;

    if( self->motor_turbo_radius_top > 1.8 )
	self->motor_turbo_radius_top = 1.8;
}

GLint
sm_boat_motor_get_speed( sm_boat_t * self )
{
    return self->motor_speed;
}

void
sm_boat_draw( sm_boat_t * boat )
{
    glPushMatrix();{
	glTranslatef( boat->pos[0],
		      boat->pos[1],
		      boat->pos[2]);
	glRotatef( boat->angle,
		   0,
		   1,
		   0 );
	
        /* Base */
	glPushMatrix();
	{
	    sm_boat_paint_base( boat );
	    glScalef( 1.0,
		      7.31/boat->base_cube_size,
		      7.92/boat->base_cube_size );
	    glutSolidCube( boat->base_cube_size );
	}glPopMatrix();

	/* Frente */
	glPushMatrix();{
	    sm_boat_paint_base( boat );
	    glTranslatef( boat->base_cube_size/2+2,
			  0.0,
			  0.0);
	    glScalef( 2.0, 7.31/2, 7.92/2 );

	    glPushMatrix();{
		glTranslatef( -1.0,
			      -1.0,
			      0.0);
		glRotatef( boat->cargo_door_angle, 0.0, 0.0, 1.0);
		glTranslatef( 1.0,
			      1.0,
			      0.0);
		/* Porta de Carga */
		glBegin( GL_QUADS );{
		    glVertex3i(1,1,1);
		    glVertex3i(-1,-1,1);
		    glVertex3i(-1,-1,-1);
		    glVertex3i(1,1,-1);
		}glEnd();
	    }glPopMatrix();

	    /* Topo */
	    glBegin( GL_QUADS );{
	    	glVertex3i(1,1,-1);
	    	glVertex3i(-1,1,-1);
	    	glVertex3i(-1,1,1);
	    	glVertex3i(1,1,1);

	    }glEnd();

	    glBegin( GL_TRIANGLES);{
	    	glVertex3i(-1,-1,1);
	    	glVertex3i(1,1,1);
	    	glVertex3i(-1,1,1);

	    	glVertex3i(-1,1,-1);
	    	glVertex3i(1,1,-1);
	    	glVertex3i(-1,-1,-1);

	    }glEnd();

	}glPopMatrix();

	/* Mastro Principal */
	glPushMatrix();{
	    sm_boat_paint_mastro( boat );
	    glTranslatef( 0.0, 7.31/2 , 0.0);
	    glRotatef( -90, 1.0,0.0,0.0);
	    gluCylinder(boat->mastro,
			boat->mastro_radius_base,
			boat->mastro_radius_top,
			boat->mastro_height,
			30,
			30);
	}glPopMatrix();

	/* Mastro Secundário */
	glPushMatrix();{
	    sm_boat_paint_mastro_sec( boat );
	    glTranslatef( 7.92, 7.31/2 , 0.0);
	    glRotatef( -90, 1.0,0.0,0.0);
	    gluCylinder(boat->mastro_sec,
			boat->mastro_sec_radius_base,
			boat->mastro_sec_radius_top,
			boat->mastro_sec_height,
			30,
			30);
	}glPopMatrix();

	/* Mastro Frontal */
	glPushMatrix();{
	    sm_boat_paint_mastro_frontal( boat );
	    glTranslatef( 7.92, 7.31/2-0.3 , 0.0);
	    glRotatef( -45, 0.0,0.0,1.0);
	    glRotatef( -90, 1.0,0.0,0.0);
	    gluCylinder(boat->mastro_frontal,
			boat->mastro_frontal_radius_base,
			boat->mastro_frontal_radius_top,
			boat->mastro_frontal_height,
			30,
			30);
	}glPopMatrix();

	/* Deck */
	glPushMatrix();{
	    sm_boat_paint_deck( boat );
	    glTranslatef( -( (boat->base_cube_size/2) -(boat->deck_cube_size/2)),
			  (7.31/2) + ((boat->deck_cube_size*0.6)/2),
			  0.0 );
	    glScalef( 1.0, 0.6, 1.0);
	    glutSolidCube( boat->deck_cube_size );
	}glPopMatrix();

	/* Quilha */
	glPushMatrix();{
	    sm_boat_paint_keel( boat );
	    glTranslatef( -( (boat->base_cube_size/2) +((boat->keel_cube_size/2)*0.5)),
			  -(7.31/2) - ((boat->keel_cube_size*0.9)/4),
			  0.0 );

	    /* Movimentação da quilha */
	    glPushMatrix();{
		glTranslatef( (boat->keel_cube_size/2)*0.5,
			      0.0,
			      0.0 );
		glRotatef(boat->keel_angle,
			  0.0,
			  1.0,
			  0.0 );
		glTranslatef( -(boat->keel_cube_size/2)*0.5,
			      0.0,
			      0.0 );
		glScalef( 0.5, 0.9, 0.07);
		glutSolidCube( boat->keel_cube_size );
	    }glPopMatrix();
	}glPopMatrix();

	/* Turbina */
	glPushMatrix();{
	    sm_boat_paint_motor( boat );
	    /* Motor 1 */
	    glPushMatrix();{
		glTranslatef( -( (boat->base_cube_size/2)),
			      0.0,
			      boat->motor_prot_radius_top );
		glRotatef( -90, 0.0,1.0,0.0);
		gluCylinder(boat->motor_prot_1,
			    boat->motor_prot_radius_base,
			    boat->motor_prot_radius_top,
			    boat->motor_prot_height,
			    30,
			    30);
		/* Hélice */
		glPushMatrix();{
		    glRotatef(boat->motor_angle, 0.0, 0.0, 1.0);
		    glScalef( 0.07, 1.0, 0.2);
		    glutSolidCube( boat->motor_hel_cube_size );
		}glPopMatrix();
		/* Turbo */
		glPushMatrix();{
		    glTranslatef( 0.0,
				  0.0,
				  boat->motor_prot_height);
		    gluCylinder(boat->motor_turbo_1,
				boat->motor_turbo_radius_base,
				boat->motor_turbo_radius_top,
				boat->motor_turbo_height,
				30,
				30);
		}glPopMatrix();
	    }glPopMatrix();

            /* Motor 2 */
	    glPushMatrix();{		
		glTranslatef( -( (boat->base_cube_size/2)),
			      0.0,
			      -boat->motor_prot_radius_top );
		glRotatef( -90, 0.0,1.0,0.0);
		gluCylinder(boat->motor_prot_2,
			    boat->motor_prot_radius_base,
			    boat->motor_prot_radius_top,
			    boat->motor_prot_height,
			    30,
			    30);
		/* Hélice */
		glPushMatrix();{
		    glRotatef(boat->motor_angle, 0.0, 0.0, 1.0);
		    glScalef( 0.07, 1.0, 0.2);
		    glutSolidCube( boat->motor_hel_cube_size );
		}glPopMatrix();

		/* Turbo */
		glPushMatrix();{
		    glTranslatef( 0.0,
				  0.0,
				  boat->motor_prot_height);
		    gluCylinder(boat->motor_turbo_2,
				boat->motor_turbo_radius_base,
				boat->motor_turbo_radius_top,
				boat->motor_turbo_height,
				30,
				30);
		}glPopMatrix();
	    }glPopMatrix();
	}glPopMatrix();

    }glPopMatrix();
}
